package com.example.common.event

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import kotlin.jvm.Throws

/**
 * 自定义LiveData事件总线
 */
object LiveDataBus {

    private val bus:MutableMap<String,BusMutableLiveData<Any>> = mutableMapOf()
    fun <T> with(key: String): BusMutableLiveData<T> {
        if (!bus.containsKey(key)){
            bus[key] = BusMutableLiveData()
        }
        return bus[key] as BusMutableLiveData<T>
    }

    /**
     *自定义LiveData
     */
    class BusMutableLiveData<T> : MutableLiveData<T>() {
        private val observerMap:MutableMap<Observer<*>,Observer<*>> = mutableMapOf()

        /**
         * 订阅前台事件
         */
        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            super.observe(owner, observer)
            try {
                hook(observer)
            }catch (e:Exception){
                e.printStackTrace()
            }

        }
        /**
         * 订阅前台粘性事件
         */
        fun observeSticky(owner:LifecycleOwner, observer: (T)->Unit){
            super.observe(owner, observer)
        }
        /**
         * 订阅后台事件
         */
        override fun observeForever(observer: Observer<in T>) {
            if(!observerMap.containsKey(observer)){
                observerMap[observer] = ObserverWrapper(observer)
            }
            super.observeForever(observerMap[observer] as Observer<in T>)
        }

        /**
         * 订阅后台粘性事件
         */
         fun observeForeverSticky(observer: Observer<in T>) {
              super.observeForever(observer)
        }
        /**
         * 取消订阅
         */
        override fun removeObserver(observer: Observer<in T>) {
            val realObserver : Observer<in T> = if(observerMap.containsKey(observer)){
                observerMap.remove(observer) as Observer<in T>
            }else{
                observer
            }
            super.removeObserver(realObserver)

        }
    }

    /**
     * 使用反射修改LiveData版本号
     */
    @Throws(Exception::class)  //注解单独抛出异常，这样不用在方法里面抛
    private fun hook(observer: Observer<*>){
        //通过反射获取到liveData这个类
        val liveDataClass = LiveData::class.java
        val mVersionField = liveDataClass.getDeclaredField("mVersion")
        mVersionField.isAccessible = true
        val version = mVersionField[this]
        //获取订阅的mVersion
        //再
        val mObservesFiled = liveDataClass.getDeclaredField("mObservers")
        mObservesFiled.isAccessible = true
        val observers = mObservesFiled[this]
        val observersClass :Class<*> = observers.javaClass
        val methodGet = observersClass.getDeclaredMethod("get", Any::class.java)
        methodGet.isAccessible = true
        //获取对应集合中实体对象
        val subObserverEntity = methodGet.invoke(observers, observer)
        var subObserverWrapper:Any? = null
        if(subObserverEntity is Map.Entry<*,*>){
            subObserverWrapper = subObserverEntity.value
        }
        if(subObserverWrapper == null){
            throw NullPointerException("observerWrapper can not  is null")
        }
        //获取观察者的父类ObserveWrapper
        val observerWrapperClass:Class<*>?= subObserverWrapper.javaClass.superclass
        val lastVersionField = observerWrapperClass!!.getDeclaredField("mLastVersion")
        lastVersionField.isAccessible = true
        //修改lastVersion
        lastVersionField[subObserverWrapper] = version
    }
}

/**
 * 自定义观察者对象
 */
private class ObserverWrapper<T>(val observer:Observer<T>):Observer<T>{
    override fun onChanged(value: T) {
        if(isCallOnObserverForever()){
            return
        }
        observer.onChanged(value)
    }
    private fun isCallOnObserverForever():Boolean{
        //获取当前的堆栈信息
        val stackTrace = Thread.currentThread().stackTrace
        for (item in stackTrace){
            if("androidx.lifecycle.LiveData" == item.className && "observeForever" == item.methodName){
                return true
            }
        }
        return false
    }

}